package com.polito.mad.bikeponyapp.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.location.Location;
import android.net.Uri;
import android.net.Uri.Builder;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

import com.polito.mad.bikeponyapp.BikePonyApplication;
import com.polito.mad.bikeponyapp.R;
import com.polito.mad.bikeponyapp.persistence.DeliveryItem;


public class ReroutingAsyncTask extends AsyncTask<List<DeliveryItem>, Void, List<DeliveryItem>>{

	private Location startLocation;
	private String message;
	private int chunkStartPos;
	private int chunkSize;
	private boolean error;
	private final int MAXIMUM_WAYPOINTS_PER_REQUEST = 8;
	private Context context;
	private AlertDialog d;
	private volatile HttpURLConnection connection;
	private boolean task_end_close_dialog;
	
	public ReroutingAsyncTask(Context context,Location startingLocation) {
		this.context=context;
		this.startLocation=new Location(startingLocation);
		chunkStartPos=0;
		chunkSize=0;
	}
	
	@Override
	protected void onPreExecute() {
		super.onPreExecute();
		onReroutingStarted();
	}
	
	@Override
	protected List<DeliveryItem> doInBackground(List<DeliveryItem>... params) {
		
		if(params.length!=1)
			return null;
		List<DeliveryItem> items = params[0];
		/*
		 * Google services for directions is limited service :  we can have a MAXIMUM number of WAYPOINTS in each request
		 * --> We need to split the query into multiple chunks and reassemble the result
		 * */
		//while(chunkCounter<items.size())
		String googleServiceUrl = "http://maps.googleapis.com/maps/api/directions/json";
		String startingPosition="";
		long startingTime = -1;
		chunkStartPos=0;
		chunkSize=0;
		if(isCancelled())
			return null;
		try 
		{
			if(startLocation!=null){
				startingPosition =String.valueOf(startLocation.getLatitude()).replaceAll(",", ".")+","+String.valueOf(startLocation.getLongitude()).replaceAll(",", ".");
			}else
			{
				error=true;
				message="Current position is not available";
				return null;
			}
			startingTime = System.currentTimeMillis();
			while(chunkStartPos<items.size())
			{
				if(isCancelled())
					return null;
				
				Builder builder = new Uri.Builder();
				builder.encodedPath(googleServiceUrl);
				
				builder.appendQueryParameter("origin", startingPosition);
				if(startingTime!=-1)
				{
					builder.appendQueryParameter("departure_time", String.valueOf(startingTime/1000));
				}
				StringBuilder wayPoints = new StringBuilder("");
				String wayPointsSeparator="|";
				/*init the chunk size*/
				chunkSize=0;
				/*a maximum of MAXIMUM_WAYPOINTS_PER_REQUEST can be put in a query*/
				for(int i=chunkStartPos; i<items.size() && chunkSize< MAXIMUM_WAYPOINTS_PER_REQUEST ;i++){
					DeliveryItem dEntry = items.get(i);
					String wayPointAddress= dEntry.getAddress() ;
					
					if(chunkSize< MAXIMUM_WAYPOINTS_PER_REQUEST-1)
						wayPoints.append(wayPointAddress).append(wayPointsSeparator);
					else
						builder.appendQueryParameter("destination", wayPointAddress);
					
					chunkSize++;
				}
				builder.appendQueryParameter("waypoints", wayPoints.toString());
				builder.appendQueryParameter("mode", "bycicling");
				builder.appendQueryParameter("sensor", "false");
				URL url = new URL(builder.build().toString());
				connection = (HttpURLConnection)url.openConnection();
				InputStream is = connection.getInputStream();
				if(is!=null){
					BufferedReader br = new BufferedReader(new InputStreamReader(is));
					StringBuilder jsonresponseBuilder = new StringBuilder();
					String line=null;
					while((line= br.readLine())!=null)
						jsonresponseBuilder.append(line);
					
					if(isCancelled())
						break;
					
					JSONObject object = new JSONObject(jsonresponseBuilder.toString());
					if(!object.getString("status").equals("OK")){
						Log.w("", "unable to redefine routes");
						message = "unable to redefine routes";
						error=true;
						break;
					}
					JSONArray routes = object.getJSONArray("routes");
					if(routes.length()==1){
						JSONArray legs = routes.getJSONObject(0).getJSONArray("legs");
						int receivedLegs = legs.length();
						if(receivedLegs!=chunkSize){
							Log.w("", "unable to redefine routes for the new list");
							message = "unable to redefine routes for the new list";
							error=true;
							break;
						}
						long expectedTime = startingTime;
						
						BikePonyApplication application =  (BikePonyApplication) context.getApplicationContext();
						application.resetMapEncodedPath();
						
						for(int i=0;i<legs.length();i++){
							
							if(isCancelled())
								break;
							
							JSONObject leg = legs.getJSONObject(i);
							int duration = leg.getJSONObject("duration").getInt("value");// in seconds
							DeliveryItem dentry = items.get(chunkStartPos+i);
							expectedTime+=duration*1000;
							dentry.setEstimatedTime(expectedTime);	
							JSONArray steps = leg.getJSONArray("steps");
							
							List<String>encPaths = new ArrayList<String>(steps.length());
							for(int j=0;j<steps.length();j++)
							{
								JSONObject step = steps.getJSONObject(j);
								encPaths.add(step.getJSONObject("polyline").getString("points"));
							}
							application.addMacEncodedPath(dentry.getId(), encPaths);
						}
						is.close();
						chunkStartPos+=chunkSize;
						DeliveryItem nextStartLeg = items.get(chunkStartPos-1);
						startingPosition=nextStartLeg.getAddress();
						startingTime = nextStartLeg.getEstimatedTime();
						// search for a least loaded agent in order to give him the work
						
					}else{
						// error : route not found
						error=true;
						break;
					}	
				}
			}
			return items;
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}catch (IOException e) {
			e.printStackTrace();
			message= "The network connection is gone";
		} catch (JSONException e) {
			e.printStackTrace();
		}
		return null;
	}
	/**
	 * This method can be called in order to get notifications about the  DeliveryItems reordered
	 * @param success
	 * @param message
	 * @param deliveryItems
	 */
	protected void onReroutingCompleted(boolean success,String message,List<DeliveryItem> deliveryItems)
	{
		
	}
	
	protected void onReroutingStarted()
	{
		d = DialogCreator.createAlertDialog(context,"Calculating the new route\n\nIt might take some times","rerouting");
        d.getWindow().setBackgroundDrawableResource(R.drawable.custom_dialog_shape);
	    d.setOnDismissListener(new DialogInterface.OnDismissListener() {
			
			@Override
			public void onDismiss(DialogInterface dialog) {
				ReroutingAsyncTask.this.cancel(true);
				if(!task_end_close_dialog)
					Toast.makeText(context, "your reouting has been cancelled", Toast.LENGTH_SHORT).show();
				
			}
		});
	    d.show();
	}
	
	@Override
	protected final void onPostExecute(List<DeliveryItem> result) {
		
		if(d!=null && d.isShowing())
		{
			task_end_close_dialog = true;
			d.dismiss();
		}
		onReroutingCompleted(result!=null && ! error, message, result);
	}
	@Override
	protected void onCancelled(List<DeliveryItem> result) {
		super.onCancelled(result);
		if(d!=null && d.isShowing())
			d.dismiss();
	}
	public interface OnReroutingListener
	{
		public void onReroutingCompleted(boolean success,String message,List<DeliveryItem> deliveryItems);
		public void onReroutingStarted();
	}
	
	public AlertDialog getDialog()
	{
		return d;
	}
	public void cancelReroutingTask()
	{
		super.cancel(true);
		connection.disconnect();
		if(d!=null && d.isShowing())
			d.dismiss();
	}
}
